home *** CD-ROM | disk | FTP | other *** search
- /*
- File: CBuffFileStream.c
-
- Contains: Buffer handling.
-
- Written by: Tim Carroll
-
- Copyright: Copyright (c) 1999 Apple Computer, Inc., All Rights Reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- */
-
- #include <Errors.h>
- #include <Files.h>
- #include "FilePermUtils.h"
- #include "CBuffFileStream.h"
-
- // flush out the buffer we have.
- // will write if it is a writing buffer
- // otherwise just empty it
- static OSErr BFSFlushOutBuffer(CBuffFileStreamData *fdata);
-
- // Check to see if we are changing from read buffering to write
- // buffering or vice-versa. If so, flush the buffer.
- static OSErr BFSFlushOnModeSwap(CBuffFileStreamData *fdata,int nowMode);
-
- // Trash the buffer. Nasty? Yes. Calling this make you lose data if you
- // were in the middle of a buffered write. Don't call it if you don't know
- // why you would.
- static void BFSTrashBuffer(CBuffFileStreamData *fdata,int nowMode);
-
- // Fill the buffer from the current location in the file. This is used when
- // the read buffer runs dry
- static OSErr BFSReadInBuffer(CBuffFileStreamData *fdata);
-
- #define DOINGDEBUGCHECKING 0
- #define STOPEACHONENTRY 0
-
- #define BYPASSBUFFER 0
-
-
- // ---------------------------------------------------------------------------
- // • BFSOpenFIle(CBuffFileStreamData **newdata, FSSpec *inFileInfo, long inPrivileges)
- // ---------------------------------------------------------------------------
- // Open a file from an FSSpec
- //
- // This opens a file given the specified permissions (fsRdWrPerm, etc.)
- // and creates a file stream buffer record.
-
- OSErr BFSOpenFile(CBuffFileStreamData **newdata, FSSpec *inFileInfo, long inPrivileges)
- {
- CBuffFileStreamData *makeBlock;
- OSErr fileErr;
-
- #if STOPEACHONENTRY
- Debugger();
- #endif
-
- if (0 == *newdata)
- return paramErr;
-
- #if DOINGDEBUGCHECKING
- if (0 != ((unsigned long) *newdata & 1))
- return paramErr;
- #endif
-
- makeBlock = (CBuffFileStreamData *) NewPtrClear(sizeof(CBuffFileStreamData));
-
- if (0 == makeBlock)
- return MemError();
-
- fileErr = FSpOpenDF(inFileInfo,inPrivileges,&(makeBlock->mFileRefNum));
- if (noErr != fileErr)
- {
- (void) DisposePtr((Ptr) makeBlock);
- *newdata = 0;
-
- return fileErr;
- }
-
- fileErr = GetPermission(makeBlock->mFileRefNum,&(makeBlock->mFilePerms));
- if (noErr != fileErr)
- {
- (void) FSClose(makeBlock->mFileRefNum);
- (void) DisposePtr((Ptr) makeBlock);
- *newdata = 0;
-
- return fileErr;
- }
-
- *newdata = makeBlock;
-
- return noErr;
- }
-
- // ---------------------------------------------------------------------------
- // • BFSCloseFile(CBuffFileStreamData *fdata)
- // ---------------------------------------------------------------------------
- // Close an opened file
- //
- // This closes an already opened buffered file.
- // It also deletes the storage associated with the file stream buffer record.
-
- OSErr BFSCloseFile(CBuffFileStreamData *fdata)
- {
- OSErr fileErr;
- OSErr file2Err;
-
- #if STOPEACHONENTRY
- Debugger();
- #endif
-
- #if DOINGDEBUGCHECKING
- if (0 != ((unsigned long) fdata & 0x1))
- return paramErr;
- #endif
-
- if (0 == fdata)
- return paramErr;
-
- fileErr = BFSFlushOutBuffer(fdata);
-
- file2Err = FSClose(fdata->mFileRefNum);
- if (noErr == fileErr)
- fileErr = file2Err;
-
- return fileErr;
- }
-
- // ---------------------------------------------------------------------------
- // • BFSSetMarker
- // ---------------------------------------------------------------------------
- // Place the Read/Write Marker at an offset from a specified position
- //
- // fromWhere can be fsAtMark (useless) or fsFromStart, fsFromLEOF, fsFromMark
-
- OSErr BFSSetMarker(CBuffFileStreamData *fdata, long inOffset, long fromWhere)
- {
- OSErr fileErr;
-
- #if STOPEACHONENTRY
- Debugger();
- #endif
-
- #if DOINGDEBUGCHECKING
- if (0 != ((unsigned long) fdata & 0x1))
- return paramErr;
- #endif
-
- if (0 == fdata)
- return paramErr;
-
- if (fromWhere < fsAtMark || fromWhere > fsFromMark)
- return paramErr;
-
- #if BYPASSBUFFER
- fileErr = SetFPos(fdata->mFileRefNum,fromWhere,inOffset);
- #else
- // Flush cache out
- fileErr = BFSFlushOutBuffer(fdata);
- if (noErr != fileErr)
- return fileErr;
-
- // Now seek
- fileErr = SetFPos(fdata->mFileRefNum,fromWhere,inOffset);
-
- // update where our window starts
- fdata->mWindowLocation = BFSGetMarker(fdata);
- #endif
-
- return fileErr;
- }
-
- long BFSGetMarker(CBuffFileStreamData *fdata)
- {
- #if DOINGDEBUGCHECKING
- OSErr fileErr;
- #endif
-
- #if STOPEACHONENTRY
- Debugger();
- #endif
-
- #if DOINGDEBUGCHECKING
- if (0 != ((unsigned long) fdata & 0x1))
- return -1;
- #endif
-
- if (0 == fdata)
- return -1;
-
- #if BYPASSBUFFER
- {
- Int32 returnMarker;
-
- fileErr = GetFPos(fdata->mFileRefNum,&returnMarker);
- if (noErr != fileErr)
- return -1;
-
- return returnMarker;
- }
- #else
- #if DOINGDEBUGCHECKING
- {
- long returnMarker;
-
- fileErr = GetFPos(fdata->mFileRefNum,&returnMarker);
- if (noErr != fileErr)
- return -1;
-
- if (returnMarker != fdata->mWindowLocation)
- DebugStr("\pmarker matching now working.");
- }
- #endif
-
- // rejigger marker
- return fdata->mWindowLocation + fdata->mBufferCurPos;
- #endif
- }
-
- // ---------------------------------------------------------------------------
- // • BFSSetLength
- // ---------------------------------------------------------------------------
- // Set the length, in bytes, of the data fork a buffered file
-
- OSErr BFSSetLength(CBuffFileStreamData *fdata, long inLength)
- {
- #if STOPEACHONENTRY
- Debugger();
- #endif
-
- #if DOINGDEBUGCHECKING
- if (0 != ((unsigned long) fdata & 0x1))
- return -1;
- #endif
-
- if (0 == fdata)
- return -1;
-
- #if BYPASSBUFFER
- return SetEOF(fdata->mFileRefNum,inLength);
- #else
- {
- OSErr fileErr;
-
- fileErr = BFSFlushOutBuffer(fdata);
- if (noErr != fileErr)
- return fileErr;
-
- // If we get an error, continue so we keep an accurate marker
- fileErr = SetEOF(fdata->mFileRefNum,inLength);
-
- fdata->mWindowLocation = BFSGetMarker(fdata);
-
- return fileErr;
- }
- #endif
- }
-
- // ---------------------------------------------------------------------------
- // • BFSGetLength
- // ---------------------------------------------------------------------------
- // Return the length, in bytes, of the data fork of a buffered file
-
- long BFSGetLength(CBuffFileStreamData *fdata)
- {
- OSErr fileErr;
-
- #if STOPEACHONENTRY
- Debugger();
- #endif
-
- #if DOINGDEBUGCHECKING
- if (0 != ((unsigned long) fdata & 0x1))
- return -1;
- #endif
-
- if (0 == fdata)
- return -1;
-
- #if BYPASSBUFFER
- {
- Int32 returnLength;
-
- // Find file system length
- fileErr = GetEOF(fdata->mFileRefNum,&returnLength);
- if (noErr != fileErr)
- return -1;
-
- return returnLength;
- }
- #else
- {
- long returnLength;
- long markerPos;
-
- // Find file system length
- fileErr = GetEOF(fdata->mFileRefNum,&returnLength);
- if (noErr != fileErr)
- return -1;
-
- // adjust length in case buffer passes current end of file
- markerPos = BFSGetMarker(fdata);
- if (-1 != markerPos && markerPos > returnLength)
- returnLength = markerPos;
-
- return returnLength;
- }
- #endif
- }
-
- // ---------------------------------------------------------------------------
- // • BFSWrite
- // ---------------------------------------------------------------------------
- // Write data from a buffer to a buffered file stream
- //
- // Returns an error code and passes back the number of bytes actually
- // written, which may be less than the number requested if an error occurred.
- // If no error is returned, the full amount was written.
-
- OSErr BFSWrite(CBuffFileStreamData *fdata, long *writeAmt, const void *inBuffer)
- {
- #if STOPEACHONENTRY
- Debugger();
- #endif
-
- #if DOINGDEBUGCHECKING
- if (0 != ((unsigned long) fdata & 0x1) || 0 != ((unsigned long) writeAmt & 0x1))
- return paramErr;
- #endif
-
- if (0 == fdata || 0 == writeAmt || *writeAmt < 0)
- return paramErr;
-
- #if BYPASSBUFFER
- return FSWrite(fdata->mfRefNum,writeAmt,inBuffer);
- #else
- {
- const void *subBuffer = inBuffer;
- long remainByteCount = *writeAmt;
- OSErr modeErr;
- long freeBytes;
-
- //
- // Road map: here's what we are doing
- //
- // 1st: Check to see if we have write permissions
- // 2nd: Is our data less the remaining section of the buffer plus an empty buffer?
- // Yes: 3rd: put as much data as you can in the current buffer
- // 4th: flush the buffer if full
- // 5th: put the rest of the data in the buffer
- // No: 3rd: Is there data already in the buffer?
- // Yes: 4th: put as much data as you can in the current buffer
- // 5th: flush the buffer
- // No: 6th: bypass the buffer to write the rest
- //
-
-
- // We only allow writing to the end of the buffer.
- // This is okay since any seeks flush the buffer and begin with a fresh one.
- // We keep mBufferCurPos in sync with mBufferLength though to make some
- // operations easier.
-
- // check for write permissions
- if (fsRdPerm == fdata->mFilePerms ||
- fsRdWrShPerm == fdata->mFilePerms)
- {
- return opWrErr; // no write permissions
- }
-
- // Check to see if we changed modes from read to write
- modeErr = BFSFlushOnModeSwap(fdata,eBuffFileStreamIamWriting);
- if (noErr != modeErr)
- return modeErr;
-
- freeBytes = eBuffFileStreamBufferMaxSize - fdata->mBufferLength;
-
- // Can we write by using the available space plus another buffer?
- if (remainByteCount < (freeBytes + eBuffFileStreamBufferMaxSize))
- {
- long addBytes = freeBytes;
-
- // Take the lesser number of the available space or the bytes we have
- if (remainByteCount < addBytes)
- addBytes = remainByteCount;
-
- // Move in the data
- BlockMoveData(subBuffer,&(fdata->mFileWindow[fdata->mBufferLength]),addBytes);
-
- // Adjust the buffer pointer, source pointer, and bytes left to write
- fdata->mBufferLength += addBytes;
- fdata->mBufferCurPos += addBytes;
- subBuffer = (const void *) ((char *) subBuffer + addBytes);
- remainByteCount -= addBytes;
-
- // Now flush the buffer if it is full
- if (eBuffFileStreamBufferMaxSize == fdata->mBufferLength)
- {
- OSErr flushErr = BFSFlushOutBuffer(fdata);
-
- if (flushErr != noErr)
- return flushErr;
- }
-
- // Now, do we have more data to write? If so, put it in the buffer
- if (remainByteCount > 0)
- {
- // Move in the data
- BlockMoveData(subBuffer,fdata->mFileWindow,remainByteCount);
-
- // Adjust the buffer pointer
- fdata->mBufferLength = remainByteCount;
- fdata->mBufferCurPos = remainByteCount;
- }
- }
- else
- {
- // We have so much data we are going to at least partially bypass the buffer.
- // First, if we have a partial buffer, we should put as much of our data as
- // possible into the buffer before we write the rest as a bypass.
- // If we don't have a partial buffer, then we would be turning one write
- // into two, and we don't want that, so skip this step and write it all
- // as a bypass.
- if (fdata->mBufferLength > 0)
- {
- OSErr flushErr;
-
- // Take as much data as we can
- BlockMoveData(subBuffer,&(fdata->mFileWindow[fdata->mBufferLength]),freeBytes);
-
- // Adjust the buffer pointer, source pointer, and bytes left to write
- fdata->mBufferLength = eBuffFileStreamBufferMaxSize;
- fdata->mBufferCurPos = eBuffFileStreamBufferMaxSize;
- subBuffer = (const void *) ((char *) subBuffer + freeBytes);
- remainByteCount -= freeBytes;
-
- // Now flush this full buffer
- flushErr = BFSFlushOutBuffer(fdata);
-
- if (flushErr != noErr)
- return flushErr;
- }
-
- // Now we are going to write the rest as a bypass
- {
- long writeCount = remainByteCount;
- OSErr bypassErr = FSWrite(fdata->mFileRefNum,&writeCount,subBuffer);
-
- if (bypassErr)
- return bypassErr;
- }
- }
-
- // We've written all we have, and we have a empty buffer (because it was
- // empty, or we flushed it), we are ready for further writes
- }
-
- *writeAmt = 0;
-
- return noErr;
- #endif // !BYPASSBUFFER
- }
-
- // ---------------------------------------------------------------------------
- // • BFSRead
- // ---------------------------------------------------------------------------
- // Read data from a buffered data stream to a buffer
- //
- // Returns an error code and passes back the number of bytes actually
- // read, which may be less than the number requested if an error occurred.
- // If no error is returned, the full amount was read.
-
- OSErr BFSRead(CBuffFileStreamData *fdata, long *readAmt, void *outBuffer)
- {
- #if STOPEACHONENTRY
- Debugger();
- #endif
-
- #if DOINGDEBUGCHECKING
- if (0 != ((unsigned long) fdata & 0x1) || 0 != ((unsigned long) readAmt & 0x1))
- return paramErr;
- #endif
-
- if (0 == fdata || 0 == readAmt || *readAmt < 0)
- return paramErr;
-
- #if BYPASSBUFFER
- return FSRead(fdata->mfRefNum,writeAmt,inBuffer);
- #else
- {
- void *subBuffer = outBuffer;
- long remainByteCount = *readAmt;
- long gotBytes = fdata->mBufferLength - fdata->mBufferCurPos;
-
- //
- // Road map: here's what we are doing
- //
- // 1st: Is our request less than the remaining data in the buffer plus another buffer?
- // Yes: 2nd: get as much data as you can/need from the current buffer
- // 3rd: need more?
- // Yes: 4th: refill buffer
- // 5th: read the rest of the data from the buffer (or eof)
- // No: 2nd: Is there data already in the buffer?
- // 3rd: get what you can from the buffer
- // 4th: bypass to read the rest
- //
-
- // Check to see if we changed modes from write to read
- OSErr modeErr = BFSFlushOnModeSwap(fdata,eBuffFileStreamIamReading);
-
- if (noErr != modeErr)
- {
- *readAmt = 0;
- return modeErr;
- }
-
- // See if we could possibly satisfy the request from the current buffer contents
- // and another less than full buffer?
-
- if (*readAmt < (gotBytes + eBuffFileStreamBufferMaxSize))
- {
- // Yes, so we want to use up what we have, read a whole buffer and
- // use part of this. However, this is written as a while loop to handle
- // operations at the end of a file.
-
- while (remainByteCount > 0)
- {
- long addBytes;
-
- // Is the buffer empty and we still need more?
- if (remainByteCount > 0 && fdata->mBufferCurPos == fdata->mBufferLength)
- {
- // Yes, refill it
- OSErr fillErr = BFSReadInBuffer(fdata);
-
- if (noErr != fillErr)
- {
- // Probably an eof, we ran out of data.
- if (fillErr == eofErr)
- *readAmt -= remainByteCount;
- else
- *readAmt = 0;
-
- return fillErr;
- }
- }
-
- addBytes = fdata->mBufferLength - fdata->mBufferCurPos;
-
- // Take the lesser number of the available data or the bytes we want
- if (remainByteCount < addBytes)
- addBytes = remainByteCount;
-
- // Move in the data
- BlockMoveData(&(fdata->mFileWindow[fdata->mBufferCurPos]),subBuffer,addBytes);
-
- // Adjust location in buffer, destination pointer, and bytes left to read
- fdata->mBufferCurPos += addBytes;
- subBuffer = (void *) ((char *) subBuffer + addBytes);
- remainByteCount -= addBytes;
-
- // Is the buffer empty and we still need more?
- if (remainByteCount > 0 && fdata->mBufferCurPos == fdata->mBufferLength)
- {
- // Yes, refill it
- OSErr fillErr = BFSReadInBuffer(fdata);
-
- if (fillErr)
- {
- // Probably an eof, we ran out of data.
- if (fillErr == eofErr)
- *readAmt -= remainByteCount;
- else
- *readAmt = 0;
-
- return fillErr;
- }
- }
- }
- }
- else
- {
- // Fast-track big read, use data available in the buffer, then switch
- // to big dedicated reads.
- long readAttempt;
- OSErr bypassErr;
-
- if (gotBytes > 0) // use the data already in the buffer
- {
- // Move in the data
- BlockMoveData(&(fdata->mFileWindow[fdata->mBufferCurPos]),subBuffer,gotBytes);
-
- // Adjust location in buffer, destination pointer, and bytes left to read
- fdata->mBufferCurPos += gotBytes;
- subBuffer = (void *) ((char *) subBuffer + gotBytes);
- remainByteCount -= gotBytes;
- }
-
- readAttempt = remainByteCount;
-
- bypassErr = FSRead(fdata->mFileRefNum,&readAttempt,subBuffer);
- if (bypassErr)
- {
- if (bypassErr == eofErr)
- {
- remainByteCount -= readAttempt;
- *readAmt -= remainByteCount;
-
- return bypassErr;
- }
- *readAmt = 0;
- return bypassErr;
- }
-
- remainByteCount -= readAttempt;
- }
-
- *readAmt -= remainByteCount;
-
- return noErr;
- }
- #endif
- }
-
- // ---------------------------------------------------------------------------
- // • BFSGetFilePermissions
- // ---------------------------------------------------------------------------
- // Find the permissions acquired when opening a buffered data file.
- //
- // This is useful if you specified fsCurPerm on open and want to know what
- // permissions you got.
-
- long BFSGetFilePermissions(CBuffFileStreamData *fdata)
- {
- #if STOPEACHONENTRY
- SysBreak();
- #endif
-
- #if DOINGDEBUGCHECKING
- if (0 != ((unsigned long) fdata & 0x1))
- return paramErr;
- #endif
-
- if (0 == fdata)
- return paramErr;
-
- return fdata->mFilePerms;
- }
-
- // ---------------------------------------------------------------------------
- // • BFSFlushFile
- // ---------------------------------------------------------------------------
- // Find the permissions acquired when opening a buffered data file.
- //
- // This is useful if you specified fsCurPerm on open and want to know what
- // permissions you got.
-
- OSErr BFSFlushFile(CBuffFileStreamData *fdata)
- {
- #if STOPEACHONENTRY
- SysBreak();
- #endif
-
- #if DOINGDEBUGCHECKING
- if (0 != ((unsigned long) fdata & 0x1))
- return paramErr;
- #endif
-
- if (0 == fdata)
- return paramErr;
-
- #if BYPASSBUFFER
- return noErr;
- #else
- {
- OSErr flushErr;
-
- flushErr = BFSFlushOutBuffer(fdata);
-
- // You should do a MacOS flush file here.
- // I'm not going to.
-
- return flushErr;
- }
- #endif
- }
-
- // flush out the buffer we have.
- // will write if it is a writing buffer
- // otherwise just empty it
- // This is not an externally accessible function so it does NOT
- // check its parameters.
- OSErr BFSFlushOutBuffer(CBuffFileStreamData *fdata)
- {
- #if BYPASSBUFFER
- return noErr;
- #else
- OSErr returnValue = noErr;
-
- if (eBuffFileStreamIamWriting == fdata->mBufferMode)
- {
- long writeAmount = fdata->mBufferLength;
-
- // write the data and update the file position
- returnValue = FSWrite(fdata->mFileRefNum,&writeAmount,fdata->mFileWindow);
-
- // update kept file position
- fdata->mWindowLocation += fdata->mBufferCurPos;
- fdata->mBufferLength = 0;
- fdata->mBufferCurPos = 0;
- }
- else
- {
- // update kept file position
- fdata->mWindowLocation += fdata->mBufferCurPos;
-
- SetFPos(fdata->mFileRefNum,fsFromStart,fdata->mWindowLocation);
-
- fdata->mBufferLength = 0;
- fdata->mBufferCurPos = 0;
- }
-
- return returnValue;
- #endif
- }
-
- // Check to see if we are changing from read buffering to write
- // buffering or vice-versa. If so, flush the buffer.
- // This is not an externally accessible function so it does NOT
- // check its parameters.
- OSErr BFSFlushOnModeSwap(CBuffFileStreamData *fdata,int nowMode)
- {
- #if BYPASSBUFFER
- return noErr;
- #else
- OSErr flushErr = noErr;
-
- if ((fdata->mBufferLength > 0) && (fdata->mBufferMode != nowMode))
- {
- flushErr = BFSFlushOutBuffer(fdata);
-
- fdata->mBufferLength = 0; // flush FOR SURE (sorry)
- fdata->mBufferCurPos = 0;
- }
-
- // set buffer mode
- fdata->mBufferMode = nowMode;
-
- return flushErr;
- #endif
- }
-
- // Trash the buffer. Nasty? Yes. Calling this make you lose data if you
- // were in the middle of a buffered write. Don't call it if you don't know
- // why you would.
- // This is not an externally accessible function so it does NOT
- // check its parameters.
- void BFSTrashBuffer(CBuffFileStreamData *fdata,int nowMode)
- {
- #if BYPASSBUFFER
- #else
- // delete the data in the buffer
- fdata->mBufferLength = 0;
- fdata->mBufferCurPos = 0;
-
- // set the buffer mode
- fdata->mBufferMode = nowMode;
- #endif
- }
-
- // Fill the buffer from the current location in the file. This is used when
- // the read buffer runs dry
- // This is not an externally accessible function so it does NOT
- // check its parameters.
- OSErr BFSReadInBuffer(CBuffFileStreamData *fdata)
- {
- OSErr returnValue = noErr;
- long readAmount = eBuffFileStreamBufferMaxSize;
-
- #if BYPASSBUFFER
- return noErr;
- #else
- #if DOINGDEBUGCHECKING
- if (eBuffFileStreamIamReading != fdata->mBufferMode)
- {
- DebugStr("\pTried to fill the buffer in write mode!");
- }
- #endif
- fdata->mWindowLocation += fdata->mBufferCurPos; // jump down the file some
-
- returnValue = FSRead(fdata->mFileRefNum,&readAmount,fdata->mFileWindow);
-
- if (eofErr == returnValue && 0 != readAmount)
- returnValue = noErr; // ignore under reads unless we run dry
-
- fdata->mBufferLength = readAmount;
- fdata->mBufferCurPos = 0;
-
- return returnValue;
- #endif
- }
-